use std::{marker::PhantomPinned, ops::DerefMut, pin::Pin};

#[test]
pub fn pin_main() -> Result<(), ()> {
    let mut i: u8 = 1;

    let _ = Pin::new(&mut i);

    Ok(())
}

#[derive(Default)]
struct AddrTracker(Option<usize>);

impl AddrTracker {
    // If we haven't checked the addr of self yet, store the current
    // address. If we have, confirm that the current address is the same
    // as it was last time, or else panic.
    fn check_for_move(&mut self) {
        let current_addr = self as *mut Self as usize;
        match self.0 {
            None => self.0 = Some(current_addr),
            Some(prev_addr) => assert_eq!(prev_addr, current_addr),
        }
    }
}

#[test]
pub fn test_tracker() {
    // Create a tracker and store the initial address
    let mut tracker = AddrTracker::default();
    tracker.check_for_move();

    // Here we shadow the variable. This carries a semantic move, and may therefore also
    // come with a mechanical memory *move*
    let mut tracker = tracker;

    // May panic!
    tracker.check_for_move();
}

#[test]
pub fn test_tracker_pin() {
    // Create a tracker and store the initial address
    let mut tracker = AddrTracker::default();

    let mut tracker = std::pin::pin!(tracker);

    // let mut tracker = Box::pin(tracker);
    // let mut tracker_p = Pin::new(&mut tracker);

    tracker.check_for_move();

    // Here we shadow the variable. This carries a semantic move, and may therefore also
    // come with a mechanical memory *move*
    // let mut tracker = tracker;

    // May panic!
    tracker.check_for_move();
}

struct PinnedTest {
    i: u8,
    _pin: PhantomPinned,
}

impl PinnedTest {
    fn chge_i(self: Pin<&mut Self>, new_i: u8) {
        unsafe {
            let slf = self.get_unchecked_mut();
            slf.i = new_i;
        }
    }

    fn chge_ii(&mut self, new_i: u8) {
        self.i = new_i;
    }
}

struct PinnedTestB {
    i: u8,
}

impl PinnedTestB {
    fn chge_i(&mut self, new_i: u8) {
        self.i = new_i;
    }
}

#[test]
pub fn test_pin_pinned() {
    let p = PinnedTest {
        i: 23,
        _pin: PhantomPinned,
    };

    // let b = Pin::new(&mut p);
    let mut b = Box::pin(p);
    // let s = b.as_mut();
    // b.chge_i(1);
    b.as_mut().chge_i(1);

    assert_eq!(b.as_ref().i, 1);

    let pb = PinnedTestB { i: 23 };
    let mut pb_b = Box::pin(pb);
    pb_b.chge_i(1);
}
